package com.ruibang.glass.quality.service.impl;

import com.google.common.collect.Lists;
import com.ruibang.glass.quality.constant.Constants;
import com.ruibang.glass.quality.constant.QualityErrorCode;
import com.ruibang.glass.quality.constant.XbarAbnormalJudgeEnums;
import com.ruibang.glass.quality.domain.req.IMRControlDiagramReq;
import com.ruibang.glass.quality.domain.req.SpcProcessDataReq;
import com.ruibang.glass.quality.domain.resp.*;
import com.ruibang.glass.quality.service.SpcService;
import com.ruibang.glass.quality.util.spc.SPCUtils;
import com.teaming.cloud.framework2.common.exception.ServiceException;
import com.teaming.cloud.framework2.common.model.ResultBody;
import io.seata.common.util.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: songJinKang
 * @CreateTime: 2023-10-19  10:51
 * @Description: TODO
 * @Version: 1.0
 */
@Slf4j
@Service
public class SpcServiceImpl implements SpcService {

    public static final double IMR_COEFFICIENT = 2.66;
    private static final double UCL_CONSTANT = 3.267;
    private static final double IN_GROUP_CONSTANT = 1.128;

    private static final Map<Integer, BigDecimal> SPC_CONSTANT_DATA = new HashMap<>();

    static {
        SPC_CONSTANT_DATA.put(2, BigDecimal.valueOf(1.128));
        SPC_CONSTANT_DATA.put(3, BigDecimal.valueOf(1.693));
        SPC_CONSTANT_DATA.put(4, BigDecimal.valueOf(2.059));
        SPC_CONSTANT_DATA.put(5, BigDecimal.valueOf(2.326));
        SPC_CONSTANT_DATA.put(6, BigDecimal.valueOf(2.534));
        SPC_CONSTANT_DATA.put(7, BigDecimal.valueOf(2.704));
        SPC_CONSTANT_DATA.put(8, BigDecimal.valueOf(2.847));
        SPC_CONSTANT_DATA.put(9, BigDecimal.valueOf(2.97));
        SPC_CONSTANT_DATA.put(10, BigDecimal.valueOf(3.078));
        SPC_CONSTANT_DATA.put(11, BigDecimal.valueOf(3.173));
        SPC_CONSTANT_DATA.put(12, BigDecimal.valueOf(3.258));
        SPC_CONSTANT_DATA.put(13, BigDecimal.valueOf(3.336));
        SPC_CONSTANT_DATA.put(14, BigDecimal.valueOf(3.407));
        SPC_CONSTANT_DATA.put(15, BigDecimal.valueOf(3.472));
        SPC_CONSTANT_DATA.put(16, BigDecimal.valueOf(3.532));
        SPC_CONSTANT_DATA.put(17, BigDecimal.valueOf(3.588));
        SPC_CONSTANT_DATA.put(18, BigDecimal.valueOf(3.640));
        SPC_CONSTANT_DATA.put(19, BigDecimal.valueOf(3.689));
        SPC_CONSTANT_DATA.put(20, BigDecimal.valueOf(3.735));
        SPC_CONSTANT_DATA.put(21, BigDecimal.valueOf(3.778));
        SPC_CONSTANT_DATA.put(22, BigDecimal.valueOf(3.819));
        SPC_CONSTANT_DATA.put(23, BigDecimal.valueOf(3.858));
    }

    public static final List<SpcXbarAbnormalJudgment> SPC_XBAR_ABNORMAL_JUDGMENT_LIST = new LinkedList<>();

    static {
        for (XbarAbnormalJudgeEnums.type value : XbarAbnormalJudgeEnums.type.values()) {
            SpcXbarAbnormalJudgment spcXbarAbnormalJudgment = new SpcXbarAbnormalJudgment();
            spcXbarAbnormalJudgment.setStandardCode(value.getCode());
            spcXbarAbnormalJudgment.setStandardDesc(value.getDesc());
            spcXbarAbnormalJudgment.setKValue(value.getDefaultKValue());
            spcXbarAbnormalJudgment.setSelectFlag(Constants.ONE_INT);
            SPC_XBAR_ABNORMAL_JUDGMENT_LIST.add(spcXbarAbnormalJudgment);
        }
    }


    /**
     * @Description: IMR控制图算法
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-23 18:20
     */
    @Override
    public ResultBody IMRAlgorithm(IMRControlDiagramReq imrControlDiagramReq) {
        //规则K值大小判断
        ruleKDetermine(imrControlDiagramReq.getSpcXbarAbnormalJudgment());

        //参数
        List<SpcXbarAbnormalJudgment> rule = imrControlDiagramReq.getSpcXbarAbnormalJudgment();
        List<BigDecimal> data = imrControlDiagramReq.getData();

        if (CollectionUtils.isNotEmpty(data)) {
            rule = rule.stream().filter(
                    spcXbarAbnormalJudgment -> XbarAbnormalJudgeEnums.selectFlag.YES.getCode().equals(spcXbarAbnormalJudgment.getSelectFlag())
            ).collect(Collectors.toList());

            //单独值
            List<BigDecimal> individualValues = new ArrayList<>();

            //移动极差
            List<BigDecimal> movingRanges = new ArrayList<>();
            // 计算单个值和移动极差
            for (int i = 0; i < data.size(); i++) {
                individualValues.add(data.get(i));
                if (i > 0) {
                    movingRanges.add(BigDecimal.valueOf(Math.abs(data.get(i).doubleValue() - data.get(i - 1).doubleValue())));
                }
            }

            //单独值
            BigDecimal meanX = SPCUtils.average(individualValues).setScale(4, BigDecimal.ROUND_HALF_UP);
            BigDecimal uclX = SPCUtils.average(individualValues).add(SPCUtils.average(movingRanges).multiply(BigDecimal.valueOf(IMR_COEFFICIENT))).setScale(4, RoundingMode.HALF_UP);
            BigDecimal lclX = SPCUtils.average(individualValues).subtract(SPCUtils.average(movingRanges).multiply(BigDecimal.valueOf(IMR_COEFFICIENT))).setScale(4, RoundingMode.HALF_UP);
            System.out.println("单独值mean：" + meanX);
            System.out.println("单独值ucl：" + uclX);
            System.out.println("单独值lcl：" + lclX);

            //移动极差
            BigDecimal meanMr = SPCUtils.average(movingRanges);
            BigDecimal uclMr = meanMr.multiply(BigDecimal.valueOf(UCL_CONSTANT)).setScale(4, BigDecimal.ROUND_HALF_UP);
            BigDecimal lclMr = BigDecimal.valueOf(0);
            System.out.println("移动极差mean：" + meanMr);
            System.out.println("移动极差ucl：" + uclMr);
            System.out.println("移动极差lcl：" + lclMr);

            //组实体
            IMRControlDiagram imrControlDiagram = new IMRControlDiagram();

            //单独值控制图
            IndividualValue individualValue = new IndividualValue();
            individualValue.setUcl(uclX);
            individualValue.setLcl(lclX);
            individualValue.setX(meanX);
            imrControlDiagram.setIndividualValue(individualValue);

            //移动极差
            MovingRange movingRange = new MovingRange();
            movingRange.setUcl(uclMr);
            movingRange.setLcl(lclMr);
            movingRange.setMr(meanMr);
            imrControlDiagram.setMovingRange(movingRange);

            //创建数据点
            individualValue.setPoint(createIMRPoint(individualValues));
            movingRange.setPoint(createIMRPoint(movingRanges));


            //处理判异
            if (CollectionUtils.isNotEmpty(rule)) {
                //规则判断  单独值
                processRule(rule, individualValues, uclX, lclX, individualValue.getPoint(), movingRange);
                //规则判断  移动极差
                processRule(rule, movingRanges, uclMr, lclMr, movingRange.getPoint(), movingRange);
            }

            //头名称
            imrControlDiagram.setHeaderName(imrControlDiagramReq.getHeaderName());

            //单独值控制图 Y轴最大值  最小值
            List<BigDecimal> iCopyData = new ArrayList<>();
            iCopyData.add(uclX);
            iCopyData.add(lclX);
            iCopyData.addAll(individualValue.getPoint().stream().map(IMRPointData::getData).collect(Collectors.toList()));
            BigDecimal diMin = iCopyData.stream().filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(new BigDecimal(0)).subtract(BigDecimal.valueOf(1));
            BigDecimal diMax = iCopyData.stream().filter(Objects::nonNull).max(BigDecimal::compareTo).orElse(new BigDecimal(0)).add(BigDecimal.valueOf(1));
            imrControlDiagram.setDiMinY(diMin);
            imrControlDiagram.setDiMaxY(diMax);

            //单独值控制图 Y轴最大值  最小值
            List<BigDecimal> mrCopyData = new ArrayList<>();
            mrCopyData.add(uclMr);
            mrCopyData.add(lclMr);
            mrCopyData.addAll(movingRange.getPoint().stream().map(IMRPointData::getData).collect(Collectors.toList()));
            BigDecimal mrMin = mrCopyData.stream().filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(new BigDecimal(0)).subtract(BigDecimal.valueOf(1));
            BigDecimal mrMax = mrCopyData.stream().filter(Objects::nonNull).max(BigDecimal::compareTo).orElse(new BigDecimal(0)).add(BigDecimal.valueOf(1));
            imrControlDiagram.setMrMinY(mrMin);
            imrControlDiagram.setMrMaxY(mrMax);


            return ResultBody.success(imrControlDiagram);
        }

        return ResultBody.success();
    }

    private void processRule(List<SpcXbarAbnormalJudgment> rule, List<BigDecimal> movingRanges, BigDecimal ucl, BigDecimal lcl, List<IMRPointData> point, MovingRange movingRange) {
        for (SpcXbarAbnormalJudgment spcXbarAbnormalJudgment : rule) {
            Integer standardCode = spcXbarAbnormalJudgment.getStandardCode();
            List<Integer> dataIndex = this.abnormalJudgment(movingRanges, spcXbarAbnormalJudgment, ucl, lcl);
            for (Integer index : dataIndex) {
                point.get(index).getAbnormalCode().add(standardCode);
            }
        }
    }

    public static BigDecimal calculateAverage(int subGroupSize, List<BigDecimal> dataList) {
        List<BigDecimal> differences = new ArrayList<>();

        int numSubGroups = dataList.size() / subGroupSize;

        for (int i = 0; i < numSubGroups; i++) {
            List<BigDecimal> subGroup = dataList.subList(i * subGroupSize, (i + 1) * subGroupSize);
            BigDecimal max = Collections.max(subGroup);
            BigDecimal min = Collections.min(subGroup);
            BigDecimal difference = max.subtract(min).abs();
            differences.add(difference);
        }

        BigDecimal sum = BigDecimal.ZERO;
        for (BigDecimal diff : differences) {
            sum = sum.add(diff);
        }

        BigDecimal avg = sum.divide(BigDecimal.valueOf(differences.size()), 4, BigDecimal.ROUND_HALF_UP);

        //获取常数表数据
        return avg.divide(SPC_CONSTANT_DATA.get(subGroupSize), 4, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * @Description: spc过程数据
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-19 14:28
     */
    @Override
    public ResultBody capability(SpcProcessDataReq spcProcessDataReq) {
        //上下限判定
        if (spcProcessDataReq.getLowerSpecLimit().doubleValue() >= spcProcessDataReq.getUpperSpecLimit().doubleValue()) {
            return ResultBody.fail(QualityErrorCode.LSL_CANNOT_GEE_USL.getCode(), QualityErrorCode.LSL_CANNOT_GEE_USL.getMsg());
        }
        //数据大小判定
        if (spcProcessDataReq.getData().size() <= 1) {
            return ResultBody.fail(QualityErrorCode.DATA_SIZE_SMALL.getCode(), QualityErrorCode.DATA_SIZE_SMALL.getMsg());
        }
        //子组大小判定
        if (!(spcProcessDataReq.getSubgroupSize() >= 1 && spcProcessDataReq.getSubgroupSize() <= 23)) {
            return ResultBody.fail(QualityErrorCode.GROUP_SIZE_SMALL.getCode(), QualityErrorCode.GROUP_SIZE_SMALL.getMsg());
        }

        //样本数据
        List<BigDecimal> data = spcProcessDataReq.getData();
        // 目标范围上限
        BigDecimal upperSpecLimit = spcProcessDataReq.getUpperSpecLimit();
        // 目标范围下限
        BigDecimal lowerSpecLimit = spcProcessDataReq.getLowerSpecLimit();


        // 均值
        BigDecimal mean = SPCUtils.average(data);


        //计算组内标准差
        BigDecimal groupStd = BigDecimal.ZERO;

        //当子组大小为1的时候
        if (spcProcessDataReq.getSubgroupSize() == 1) {
            //单独值 移动极差容器
            List<BigDecimal> movingRanges = new ArrayList<>();
            for (int i = 0; i < data.size(); i++) {
                if (i > 0) {
                    movingRanges.add(BigDecimal.valueOf(Math.abs(data.get(i).doubleValue() - data.get(i - 1).doubleValue())));
                }
            }
            //组内标准差
            groupStd = SPCUtils.average(movingRanges).divide(BigDecimal.valueOf(IN_GROUP_CONSTANT), 4, BigDecimal.ROUND_HALF_UP);
        } else {
            //大于1的情况
            groupStd = calculateAverage(spcProcessDataReq.getSubgroupSize(), data);
            log.error(groupStd.toString());
        }


        // 标准差
        BigDecimal wStD = SPCUtils.standardDiviation(data);
        // 过程性能指数 PP
        BigDecimal processPerformanceIndex = upperSpecLimit.subtract(lowerSpecLimit).divide(wStD.multiply(BigDecimal.valueOf(6)), 4, BigDecimal.ROUND_HALF_UP);
        // 下限过程性能指数 PPL
        BigDecimal processPerformanceIndexLower = mean.subtract(lowerSpecLimit).divide(wStD.multiply(BigDecimal.valueOf(3)), 4, BigDecimal.ROUND_HALF_UP);
        // 上限过程性能指数 PPU
        BigDecimal processPerformanceIndexUpper = upperSpecLimit.subtract(mean).divide(wStD.multiply(BigDecimal.valueOf(3)), 4, BigDecimal.ROUND_HALF_UP);
        // 过程能力指数 PPK
        BigDecimal processCapabilityIndex = BigDecimal.valueOf(Math.min(processPerformanceIndexLower.doubleValue(), processPerformanceIndexUpper.doubleValue()));
        // 中心化参数 Cpm
        BigDecimal centeringParameter = mean.subtract(upperSpecLimit.add(lowerSpecLimit).divide(BigDecimal.valueOf(2), 4, BigDecimal.ROUND_HALF_UP)).divide(upperSpecLimit.subtract(lowerSpecLimit).divide(BigDecimal.valueOf(2), 4, BigDecimal.ROUND_HALF_UP), 4, BigDecimal.ROUND_HALF_UP);


        System.out.println("平均值：" + mean);
        System.out.println("整体标准差：：" + wStD);
        System.out.println("过程性能指数 PP: " + processPerformanceIndex);
        System.out.println("下限过程性能指数 PPL: " + processPerformanceIndexLower);
        System.out.println("上限过程性能指数 PPU: " + processPerformanceIndexUpper);
        System.out.println("过程能力指数 PPK: " + processCapabilityIndex);
        System.out.println("中心化参数 Cpm: " + centeringParameter);


        //组实体
        CapabilityDiagram capabilityDiagram = new CapabilityDiagram();
        capabilityDiagram.setLsl(lowerSpecLimit);
        capabilityDiagram.setTarget("*");
        capabilityDiagram.setUsl(upperSpecLimit);
        capabilityDiagram.setMean(mean.setScale(4, BigDecimal.ROUND_HALF_UP));
        capabilityDiagram.setCount(spcProcessDataReq.getData().size());
        capabilityDiagram.setGroupStandardDeviation(groupStd.setScale(4, BigDecimal.ROUND_HALF_UP));
        capabilityDiagram.setWholeStandardDeviation(wStD.setScale(4, BigDecimal.ROUND_HALF_UP));
        capabilityDiagram.setCp(BigDecimal.valueOf(SPCUtils.cp(upperSpecLimit.doubleValue(), lowerSpecLimit.doubleValue(), groupStd.doubleValue())).setScale(4, BigDecimal.ROUND_HALF_UP));
        capabilityDiagram.setCpl(BigDecimal.valueOf(SPCUtils.cpl(mean.doubleValue(), lowerSpecLimit.doubleValue(), groupStd.doubleValue())).setScale(4, BigDecimal.ROUND_HALF_UP));
        capabilityDiagram.setCpu(BigDecimal.valueOf(SPCUtils.cpu(upperSpecLimit.doubleValue(), mean.doubleValue(), groupStd.doubleValue())).setScale(4, BigDecimal.ROUND_HALF_UP));
        capabilityDiagram.setCpk(BigDecimal.valueOf(SPCUtils.cpk(upperSpecLimit.doubleValue(), lowerSpecLimit.doubleValue(), mean.doubleValue(), groupStd.doubleValue())).setScale(4, BigDecimal.ROUND_HALF_UP));
        capabilityDiagram.setPp(processPerformanceIndex);
        capabilityDiagram.setPpl(processPerformanceIndexLower);
        capabilityDiagram.setPpu(processPerformanceIndexUpper);
        capabilityDiagram.setPpk(processCapabilityIndex);
        capabilityDiagram.setCpm(centeringParameter);


        //绘制分布图
        capabilityDiagram.setWholeProfilePoint(drawProfilePoint(mean.doubleValue(), wStD.doubleValue()));
        capabilityDiagram.setGroupProfilePoint(drawProfilePoint(mean.doubleValue(), groupStd.doubleValue()));


        List<BigDecimal> copyData = new ArrayList<>();
        copyData.addAll(data);
        copyData.addAll(capabilityDiagram.getWholeProfilePoint().stream().map(ProfilePoint::getX).collect(Collectors.toList()));
        //样本数据最小值
        BigDecimal min = copyData.stream().filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(new BigDecimal(0));
        //样本数据最大值
        BigDecimal max = copyData.stream().filter(Objects::nonNull).max(BigDecimal::compareTo).orElse(new BigDecimal(0));


        //柱状图刻度
        List<Integer> calculateXTicks = calculateXTicks(calculateNumBins(data.size()), max.doubleValue(), min.doubleValue());
        capabilityDiagram.setScale(calculateXTicks);


        //数据范围
        List<DataRange> dataRanges = new ArrayList<>();
        for (int i = 0; i < calculateXTicks.size(); i++) {
            if (i + 1 < calculateXTicks.size()) {
                Integer iLeft = calculateXTicks.get(i);
                Integer iRight = calculateXTicks.get(i + 1);
                int scopeCount = getScopeCount(iLeft, iRight, data);
                System.out.println(scopeCount);
                DataRange dataRange = new DataRange();
                dataRange.setMax(BigDecimal.valueOf(iRight));
                dataRange.setMin(BigDecimal.valueOf(iLeft));
                dataRange.setCount(scopeCount);
                dataRanges.add(dataRange);
            }
        }
        capabilityDiagram.setRange(dataRanges);


        //Y轴最大值
        Integer yMax = dataRanges.stream().map(DataRange::getCount).filter(Objects::nonNull).max(Integer::compareTo).orElse(0);
        capabilityDiagram.setYMax(BigDecimal.valueOf(yMax.longValue()));


        return ResultBody.success(capabilityDiagram);
    }


    /**
     * @Description: 判异
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-23 1:52
     */
    @Override
    public List<Integer> abnormalJudgment(List<BigDecimal> datas, SpcXbarAbnormalJudgment spcXbarAbnormalJudgments, BigDecimal ucl, BigDecimal lcl) {
        // 用于存储异常数据的下标
        List<Integer> abnormalDataIndexs = Lists.newArrayList();

        //中心线-总均值
        BigDecimal avgX = SPCUtils.average(datas);

        //标准差
        BigDecimal standardDiviation = BigDecimal.valueOf(Math.abs(avgX.subtract(ucl).divide(BigDecimal.valueOf(3), 4, BigDecimal.ROUND_HALF_UP).doubleValue()));


        // 数据计算容器
        // 1：中心线上方。-1：中心线下方 （标准2）
        int twoDirection = 1;
        // 累计异常数据下标（标准2）
        List<Integer> twoAbnormalIndex = Lists.newArrayList();
        // 1：上升   -1：下降 （标准3）
        int threeDirection = 1;
        // 累计异常数据下标（标准3）
        List<Integer> threeAbnormalIndex = Lists.newArrayList();
        // 1：上升   -1：下降 （标准4）
        int fourDirection = 1;
        // 累计异常数据下标（标准4）
        List<Integer> fourAbnormalIndex = Lists.newArrayList();
        // 累计异常数据下标（标准7）
        List<Integer> sevenAbnormalIndex = Lists.newArrayList();
        // 累计异常数据下标（标准8）
        List<Integer> eightAbnormalIndex = Lists.newArrayList();

        for (int i = 0; i < datas.size(); i++) {
            //值
            BigDecimal data = datas.get(i);
            //k值
            BigDecimal k = new BigDecimal(spcXbarAbnormalJudgments.getKValue());
            //距离中心线的差
            double centerDifference = Math.abs(data.doubleValue() - avgX.doubleValue());

            switch (XbarAbnormalJudgeEnums.type.getByCode(spcXbarAbnormalJudgments.getStandardCode())) {
                // 一个点，距离中心线大于K个标准差
                case ONE:
                    if (centerDifference > k.multiply(standardDiviation).doubleValue()) {
                        abnormalDataIndexs.add(i);
                    }
                    break;
                // 连续K个点在中心线同一侧
                case TWO:
                    if (k.intValue() > 1) {
                        if (i == 0) {
                            twoDirection = data.compareTo(avgX) > 0 ? 1 : -1;
                            twoAbnormalIndex.add(i);
                        } else {
                            // 跟上个数据在同一侧，累加
                            if ((data.compareTo(avgX) > 0 && twoDirection > 0) || (data.compareTo(avgX) < 0 && twoDirection < 0)) {
                                twoAbnormalIndex.add(i);
                                //第一次到达阈值，把前面符合条件的数据都存入警告集合；
                                if (twoAbnormalIndex.size() == k.intValue()) {
                                    abnormalDataIndexs.add(twoAbnormalIndex.get(twoAbnormalIndex.size() - 1));
                                } else if (twoAbnormalIndex.size() > k.intValue()) {
                                    abnormalDataIndexs.add(i);
                                }
                            }
                            // 不符合报警条件，归零重新计算
                            else {
                                twoDirection = data.compareTo(avgX) > 0 ? 1 : -1;
                                twoAbnormalIndex.clear();
                                twoAbnormalIndex.add(i);
                            }
                        }
                    }
                    break;
                // 连续K个点， 全部递增或全部递减
                case THREE:
                    if (k.intValue() > 1) {
                        // 判断方向至少需要两个点，初始值默认要存两个，从第三个开始判断是否递增递减；
                        if (i < 2) {
                            threeAbnormalIndex.add(i);
                        } else {
                            // 跟上个数据同个趋势，累加
                            if ((data.compareTo(datas.get(i - 1)) > 0 && threeDirection > 0) || (data.compareTo(datas.get(i - 1)) < 0 && threeDirection < 0)) {
                                threeAbnormalIndex.add(i);
                                //第一次到达阈值，把前面符合条件的数据都存入警告集合；
//                                    if (threeAbnormalIndex.size() == k.intValue()) {
//                                        abnormalDataIndexs.add(threeAbnormalIndex.get(threeAbnormalIndex.size() - 1));
//                                    } else if (threeAbnormalIndex.size() > k.intValue()) {
//                                        abnormalDataIndexs.add(i);
//                                    }
                                if (threeAbnormalIndex.size() > k.intValue()) {
                                    abnormalDataIndexs.add(threeAbnormalIndex.get(threeAbnormalIndex.size() - 1));
                                }
                            }
                            // 不符合报警条件，归零重新计算
                            else {
                                threeDirection = data.compareTo(datas.get(i - 1)) > 0 ? 1 : -1;
                                threeAbnormalIndex.clear();
                                threeAbnormalIndex.add(i - 1);
                                threeAbnormalIndex.add(i);
                            }
                        }
                    }
                    break;
                // 连续K个点，上下交接
                case FOUR:
                    if (k.intValue() > 1) {
                        if (i < 2) {
                            // 判断方向至少需要两个点，初始值默认要存两个，从第三个开始判断是否递增递减；
                            fourAbnormalIndex.add(i);
                        } else {
                            // 跟上个数据不同趋势，累加
                            if ((data.compareTo(datas.get(i - 1)) > 0 && fourDirection < 0) || (data.compareTo(datas.get(i - 1)) < 0 && fourDirection > 0)) {
                                // 方向重置
                                fourDirection = data.compareTo(datas.get(i - 1)) > 0 ? 1 : -1;
                                fourAbnormalIndex.add(i);
                                //第一次到达阈值，把前面符合条件的数据都存入警告集合；
//                                    if (fourAbnormalIndex.size() == k.intValue()) {
//                                        abnormalDataIndexs.add(fourAbnormalIndex.get(fourAbnormalIndex.size() - 1));
//                                    } else if (fourAbnormalIndex.size() > k.intValue()) {
//                                        abnormalDataIndexs.add(i);
//                                    }
                                if (fourAbnormalIndex.size() > k.intValue()) {
                                    abnormalDataIndexs.add(fourAbnormalIndex.get(fourAbnormalIndex.size() - 1));
                                }
                            } else {
                                // 不符合报警条件，归零重新计算
                                fourDirection = data.compareTo(datas.get(i - 1)) > 0 ? 1 : -1;
                                fourAbnormalIndex.clear();
                                fourAbnormalIndex.add(i - 1);
                                fourAbnormalIndex.add(i);
                            }
                        }
                    }
                    break;
                // K+1个点中有K个点，距离中心线（同侧）大于2个标准差
                case FIVE:
                    // 中心线上方累计异常数据下标（标准5）
                    List<Integer> fiveUpAbnormalIndex = Lists.newArrayList();
                    // 中心线下方累计异常数据下标（标准5）
                    List<Integer> fiveDownAbnormalIndex = Lists.newArrayList();
                    // 从第K个值开始统计，往前遍历是否有K-1个值符合条件
                    if (i > k.intValue() - 1) {
                        for (int j = 0; j <= k.intValue(); j++) {
                            double dData = datas.get(i - j).doubleValue();
                            if (Math.abs(avgX.doubleValue() - dData) > standardDiviation.doubleValue() * 2) {
                                if (dData >= avgX.doubleValue()) {
                                    fiveUpAbnormalIndex.add(i - j);
                                } else {
                                    fiveDownAbnormalIndex.add(i - j);
                                }
                            }
                        }
                        if (fiveUpAbnormalIndex.size() >= k.intValue()) {
                            abnormalDataIndexs.add(fiveUpAbnormalIndex.get(0));
                        }
                        if (fiveDownAbnormalIndex.size() >= k.intValue()) {
                            abnormalDataIndexs.add(fiveDownAbnormalIndex.get(0));
                        }
                    }
                    break;
                // K+1个点中有K个点，距离中心线（同侧）大于1个标准差
                case SIX:
                    // 中心线上方累计异常数据下标（标准6）
                    List<Integer> sixUpAbnormalIndex = Lists.newArrayList();
                    // 中心线下方累计异常数据下标（标准6）
                    List<Integer> sixDownAbnormalIndex = Lists.newArrayList();
                    // 从第K个值开始统计，往前遍历是否有K-1个值符合条件
                    if (i > k.intValue() - 1) {
                        for (int j = 0; j <= k.intValue(); j++) {
                            double dData = datas.get(i - j).doubleValue();
                            if (Math.abs(avgX.doubleValue() - dData) > standardDiviation.doubleValue()) {
                                if (dData >= avgX.doubleValue()) {
                                    sixUpAbnormalIndex.add(i - j);
                                } else {
                                    sixDownAbnormalIndex.add(i - j);
                                }
                            }
                        }
                        if (sixUpAbnormalIndex.size() >= k.intValue()) {
                            abnormalDataIndexs.add(sixUpAbnormalIndex.get(0));
                        }
                        if (sixDownAbnormalIndex.size() >= k.intValue()) {
                            abnormalDataIndexs.add(sixDownAbnormalIndex.get(0));

                        }
                    }
                    break;
                // 连续K个点，距离中心线（任一侧）1个标准差以内
                case SEVEN:
                    sevenEightMethod(abnormalDataIndexs, sevenAbnormalIndex, i, k, centerDifference < standardDiviation.doubleValue(), standardDiviation, data);
                    break;
                // 连续K个点，距离中心线（任一侧）大于1个标准差
                case EIGHT:
                    sevenEightMethod(abnormalDataIndexs, eightAbnormalIndex, i, k, centerDifference > standardDiviation.doubleValue(), standardDiviation, data);
                    break;
                default:
                    break;
            }
        }
        Collections.sort(abnormalDataIndexs);
        abnormalDataIndexs = abnormalDataIndexs.stream().distinct().collect(Collectors.toList());
        return abnormalDataIndexs;
    }

    private void sevenEightMethod(List<Integer> abnormalDataIndexs, List<Integer> eightAbnormalIndex, int i, BigDecimal k, boolean b, BigDecimal standardDiviation, BigDecimal data) {
        if (b) {
            eightAbnormalIndex.add(i);
            //第一次到达阈值，把前面符合条件的数据都存入警告集合
            if (eightAbnormalIndex.size() == k.intValue()) {
                abnormalDataIndexs.add(eightAbnormalIndex.get(eightAbnormalIndex.size() - 1));
            } else if (eightAbnormalIndex.size() > k.intValue()) {
                abnormalDataIndexs.add(i);
            }
        } else {
            // 不符合报警条件，归零重新计算
            eightAbnormalIndex.clear();
        }
    }


    @Override
    public int getScopeCount(int iLeft, int iRight, List<BigDecimal> data) {
        return (int) data.stream().filter(e -> {
            if (e.doubleValue() > iLeft && e.doubleValue() <= iRight) {
                return true;
            }
            return false;
        }).count();
    }


    private static int calculateNumBins(Integer size) {
        return (int) Math.ceil(2 * Math.pow(size, 1.0 / 3));
    }


    private static List<Integer> calculateXTicks(int numBins, double maxValue, double minValue) {
        double binWidth = (maxValue - minValue) / numBins;
        List<Integer> xTicks = new ArrayList<>();
        for (int i = 0; i <= numBins; i++) {
            double x = minValue + i * binWidth;
            if (x > 0) {
                xTicks.add((int) Math.ceil(x));
            } else {
                xTicks.add((int) Math.floor(x));
            }
        }
        if (!xTicks.contains(0)) {
            xTicks.add(0);
        }
        Collections.sort(xTicks);
        return xTicks;
    }


    private static double getIQR(double[] data) {
        Arrays.sort(data);
        int q1Index = (int) Math.floor(data.length * 0.25);
        int q3Index = (int) Math.ceil(data.length * 0.75);
        return data[q3Index] - data[q1Index];
    }

    private static double getStdDev(double[] data) {
        double sum = 0.0;
        double mean = getMean(data);
        for (double value : data) {
            sum += Math.pow(value - mean, 2);
        }
        return Math.sqrt(sum / (data.length - 1));
    }

    private static double getMean(double[] data) {
        double sum = 0.0;
        for (double value : data) {
            sum += value;
        }
        return sum / data.length;
    }

    /**
     * @Description: 获取IMR数据检验项
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-23 1:40
     */
    @Override
    public ResultBody getIMRInspectionItems() {
        return ResultBody.success(SPC_XBAR_ABNORMAL_JUDGMENT_LIST);
    }

    private List<IMRPointData> createIMRPoint(List<BigDecimal> data) {
        List<IMRPointData> dataPoint = new ArrayList<>();
        for (BigDecimal datum : data) {
            //创建点
            IMRPointData imrPointData = new IMRPointData();
            imrPointData.setData(datum);
            imrPointData.setAbnormalCode(new ArrayList<>());
            dataPoint.add(imrPointData);
        }
        return dataPoint;
    }


    /**
     * @Description: 绘制正态分布图
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-26 16:44
     */
    @Override
    public List<ProfilePoint> drawProfilePoint(double mean, double standardDeviation) {
        System.out.println("mean :{}" + mean + "  std: {}" + standardDeviation);
        List<ProfilePoint> point = new ArrayList<>();
        // 创建正态分布对象
        NormalDistribution normalDistribution = new NormalDistribution(mean, standardDeviation);
        //总长
        double sumLen = mean + 3 * standardDeviation;
        //步距
        double stepDistance = 0.1;
        if (sumLen > 300 && sumLen <= 800) {
            stepDistance = 1.0;
        } else if (sumLen > 800) {
            stepDistance = 2.0;
        }
        //绘点
        for (double x = mean - 3 * standardDeviation; x <= sumLen; x += stepDistance) {
            double y = normalDistribution.density(x);
            BigDecimal dataX = BigDecimal.valueOf(x).setScale(4, BigDecimal.ROUND_HALF_UP);
            BigDecimal dataY = BigDecimal.valueOf(y).setScale(4, BigDecimal.ROUND_HALF_UP);
            System.out.println(dataX + "   --   " + dataY);
            ProfilePoint profilePoint = new ProfilePoint();
            profilePoint.setX(dataX);
            profilePoint.setY(dataY);
            point.add(profilePoint);
        }
        return point;
    }


    /**
     * @Description: 规则key判断
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-30 15:40
     */
    private void ruleKDetermine(List<SpcXbarAbnormalJudgment> spcXbarAbnormalJudgment) {
        for (SpcXbarAbnormalJudgment xbarAbnormalJudgment : spcXbarAbnormalJudgment) {
            Integer k = xbarAbnormalJudgment.getKValue();
            switch (XbarAbnormalJudgeEnums.type.getByCode(xbarAbnormalJudgment.getStandardCode())) {
                // 一个点，距离中心线大于K个标准差
                case ONE:
                    if (!(k >= 1 && k <= 6)) {
                        throw new ServiceException(1, "错误：1 <= k >= 6");
                    }
                    break;
                // 连续K个点在中心线同一侧
                case TWO:
                    if (!(k >= 7 && k <= 11)) {
                        throw new ServiceException(2, "错误：7 <= k >= 11");
                    }
                    break;
                // 连续K个点， 全部递增或全部递减
                case THREE:
                    if (!(k >= 5 && k <= 8)) {
                        throw new ServiceException(3, "错误：5 <= k >= 8");
                    }
                    break;
                // 连续K个点，上下交接
                case FOUR:
                    if (!(k >= 12 && k <= 14)) {
                        throw new ServiceException(4, "错误：12 <= k >= 14");
                    }
                    break;
                // K+1个点中有K个点，距离中心线（同侧）大于2个标准差
                case FIVE:
                    if (!(k >= 2 && k <= 4)) {
                        throw new ServiceException(5, "错误：2 <= k >= 4");
                    }
                    break;
                // K+1个点中有K个点，距离中心线（同侧）大于1个标准差
                case SIX:
                    if (!(k >= 3 && k <= 6)) {
                        throw new ServiceException(6, "错误：3 <= k >= 6");
                    }
                    break;
                // 连续K个点，距离中心线（任一侧）1个标准差以内
                case SEVEN:
                    if (!(k >= 12 && k <= 15)) {
                        throw new ServiceException(7, "错误：12 <= k >= 15");
                    }
                    break;
                // 连续K个点，距离中心线（任一侧）大于1个标准差
                case EIGHT:
                    if (!(k >= 6 && k <= 10)) {
                        throw new ServiceException(8, "错误：6 <= k >= 10");
                    }
                    break;
                default:
                    throw new ServiceException(9, "请选择正确判异规则！");
            }
        }
    }


}
